package com.ht.api.service;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.springframework.stereotype.Component;

import com.ht.api.bean.AjaxResult;
import com.ht.api.bean.UserInfo;
import com.ht.api.constant.CacheConstants;
import com.ht.api.constant.Constants;
import com.ht.api.constant.UserConstants;
import com.ht.api.db.Db;
import com.ht.api.db.DbList;
import com.ht.api.db.Entity;
import com.ht.api.db.exception.HtException;
import com.ht.api.db.util.RedisUtil;
import com.ht.api.enums.Status;
import com.ht.api.util.JwtUtils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 登录相关
 * @author asq
 * @createTime 2024年12月20日 20:08:02
 */
@Component
@Slf4j
public class LoginService {
	
	/**
	 * 登录
	 * @param username
	 * @param password
	 * @return 返回登陆后的详细信息
	 */
	public AjaxResult login(String username, String password) {
		// 1. 验证码校验
		//validateCaptcha(username, code, uuid);

		// 2. 登录前置校验
		loginPreCheck(username, password);

		// 3. 错误次数校验
		Integer retryCount = RedisUtil.get(getCacheKey(username));
		if (retryCount == null) {
			retryCount = 0;
		}
		if (retryCount >= Constants.LOGIN_MAX_RETRY_COUNT) {
			throw new HtException(
					String.format("密码输入错误%s次，帐户锁定%s分钟", Constants.LOGIN_MAX_RETRY_COUNT, Constants.LOGIN_LOCK_TIME));
		}

		// 4. 密码校验
		Entity user = Db.createSqlParser("sys_user").where("user_name", username).findOne();
		if (user == null) {
			log.info("登录用户：{} 不存在.", username);
			throw new HtException("用户不存在/密码错误");
		} else if (user.getInt("status") == Status.DISABLE.getCode()) {
			log.info("登录用户：{} 已被停用.", username);
			throw new HtException("用户已封禁，请联系管理员");
		}
		if (!StrUtil.equals(SecureUtil.md5(password), user.getStr("password"))) {
			retryCount = retryCount + 1;
			RedisUtil.set(getCacheKey(username), retryCount, Constants.LOGIN_LOCK_TIME, TimeUnit.MINUTES);
			throw new HtException("用户不存在/密码错误");
		} else {
			// 创建token
			String uuid = UUID.randomUUID().toString();
			Map<String, Object> payload = new HashMap<>();
			payload.put("username", username);
			payload.put("uuid", uuid);
			String token = JwtUtils.createToken(payload);

			// 登陆对象写入缓存
			UserInfo userInfo = new UserInfo(user, uuid);
			RedisUtil.set(CacheConstants.KEY_USER + username, JSONUtil.toJsonStr(userInfo), CacheConstants.EXPIRE_TOKEN);
			RedisUtil.set(CacheConstants.KEY_USER_STAT_WEB + userInfo.getId(), userInfo.getStatus(), CacheConstants.EXPIRE_TOKEN);
			
			// 写入登陆日志
			//htTaskExecutor.execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登陆成功"));
			
			// 获取登录用户的菜单列表
			DbList menuList = null;
			if (userInfo.isAdmin()) {
				menuList = Db.createSqlParser("sys_menu").order("order_num", "desc").order("sys_time", "asc").query();
			} else {
				StringBuilder sb = new StringBuilder();
				sb.append("select * from sys_menu where sys_deleted=1 and main_id in(");
				sb.append("  select menu_id from sys_role_menu where sys_deleted=1 and role_id in(");
				sb.append("    select role_id from sys_user_role where sys_deleted=1 and user_id=?");
				sb.append("  )");
				sb.append(")order by order_num desc,sys_time asc");
				menuList = Db.query(sb.toString(), userInfo.getId());
			}
			
			// 去掉登录用户的一些敏感信息，然后返回给前端
			user.remove("password");
			
			return AjaxResult.success().put("token", token).put("menus", menuList)
					.put("info", user);
		}
	}
	
	/**
	 * 登录账户密码错误次数缓存键名
	 * @param username 用户名
	 * @return 缓存键key
	 */
	private String getCacheKey(String username) {
		return String.format("%s%s", CacheConstants.KEY_PWD_ERR_CNT, username);
	}

	/**
	 * 登录前置校验
	 * @param username 用户名
	 * @param password 用户密码
	 */
	private void loginPreCheck(String username, String password) {
		// 用户名或密码为空 错误
		if (StrUtil.isEmpty(username) || StrUtil.isEmpty(password)) {
			//htTaskExecutor.execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名或密码为空"));
			throw new HtException("用户名或密码为空");
		}
		// 密码如果不在指定范围内 错误
		if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
				|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
			//htTaskExecutor.execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, "用户不存在/密码错误"));
			throw new HtException("用户不存在/密码错误");
		}
		// 用户名不在指定范围内 错误
		if (username.length() < UserConstants.USERNAME_MIN_LENGTH
				|| username.length() > UserConstants.USERNAME_MAX_LENGTH) {
			//htTaskExecutor.execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, "用户不存在/密码错误"));
			throw new HtException("用户不存在/密码错误");
		}
	}
}
